home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmcd-1.4 / libdi.d / os_svr4.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-10  |  14.4 KB  |  673 lines

  1. /*
  2.  *   libdi - CD Audio Player Device Interface Library
  3.  *
  4.  *   Copyright (C) 1995  Ti Kan
  5.  *   E-mail: ti@amb.org
  6.  *
  7.  *   This program is free software; you can redistribute it and/or modify
  8.  *   it under the terms of the GNU General Public License as published by
  9.  *   the Free Software Foundation; either version 2 of the License, or
  10.  *   (at your option) any later version.
  11.  *
  12.  *   This program is distributed in the hope that it will be useful,
  13.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *   GNU General Public License for more details.
  16.  *
  17.  *   You should have received a copy of the GNU General Public License
  18.  *   along with this program; if not, write to the Free Software
  19.  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  */
  22. #ifndef LINT
  23. static char *_os_svr4_c_ident_ = "@(#)os_svr4.c    5.10 94/12/28";
  24. #endif
  25.  
  26. #include "common.d/appenv.h"
  27. #include "common.d/util.h"
  28. #include "libdi.d/libdi.h"
  29. #include "libdi.d/scsipt.h"
  30.  
  31. #if defined(SVR4) && !defined(sun) && \
  32.     defined(DI_SCSIPT) && !defined(DEMO_ONLY)
  33.  
  34. extern appdata_t    app_data;
  35. extern bool_t        scsipt_notrom_error;
  36. extern FILE        *errfp;
  37.  
  38. STATIC int        pthru_fd = -1;    /* Passthrough device file desc */
  39.  
  40.  
  41. #if defined(i386) || (defined(_FTX) && defined(__hppa))
  42. /*
  43.  *   USL UNIX SVR4/x86 support
  44.  *   Stratus UNIX SVR4/PA-RISC FTX 3.x support
  45.  *   Portable Device Interface/SCSI Device Interface
  46.  *
  47.  *   This software fragment contains code that interfaces the
  48.  *   CD player application to the UNIX System V Release 4
  49.  *   operating system for the Intel x86 hardware platforms and
  50.  *   Stratus PA-RISC systems.  The name "USL", "UNIX", "Intel",
  51.  *   "Stratus" and "PA-RISC" are used here for identification
  52.  *   purposes only.
  53.  */
  54.  
  55.  
  56. STATIC char        ptpath[FILE_PATH_SZ] = { '\0' };
  57.                     /* Passthrough device path */
  58. STATIC req_sense_data_t    sense_data;    /* Request sense data buffer */
  59.  
  60.  
  61. /*
  62.  * pthru_send
  63.  *    Build SCSI CDB and send command to the device.
  64.  *
  65.  * Args:
  66.  *    opcode - SCSI command opcode
  67.  *    addr - The "address" portion of the SCSI CDB
  68.  *    buf - Pointer to data buffer
  69.  *    size - Number of bytes to transfer
  70.  *    rsvd - The "reserved" portion of the SCSI CDB
  71.  *    length - The "length" portion of the SCSI CDB
  72.  *    param - The "param" portion of the SCSI CDB
  73.  *    control - The "control" portion of the SCSI CDB
  74.  *    rw - Data transfer direction flag (READ_OP or WRITE_OP)
  75.  *    prnerr - Whether an error message should be displayed
  76.  *         when a command fails
  77.  *
  78.  * Return:
  79.  *    TRUE - command completed successfully
  80.  *    FALSE - command failed
  81.  */
  82. bool_t
  83. pthru_send(
  84.     byte_t        opcode,
  85.     word32_t    addr,
  86.     byte_t        *buf,
  87.     word32_t    size,
  88.     byte_t        rsvd,
  89.     word32_t    length,
  90.     byte_t        param,
  91.     byte_t        control,
  92.     byte_t        rw,
  93.     bool_t        prnerr
  94. )
  95. {
  96.     struct sb    sb;
  97.     struct scb    *scbp;
  98.     union scsi_cdb    cdb;
  99.     
  100.     if (pthru_fd < 0 || scsipt_notrom_error)
  101.         return FALSE;
  102.  
  103.     memset(&sense_data, 0, sizeof(sense_data));
  104.     memset(&sb, 0, sizeof(sb));
  105.     memset(&cdb, 0, sizeof(cdb));
  106.  
  107.     sb.sb_type = ISCB_TYPE;
  108.     scbp = &sb.SCB;
  109.  
  110.     /* set up SCSI CDB */
  111.     switch (opcode & 0xf0) {
  112.     case 0xa0:
  113.     case 0xe0:
  114.         /* 12-byte commands */
  115.         cdb.scl.sl_op = opcode;
  116.         cdb.scl.sl_res1 = param;
  117.         cdb.scl.sl_lun = 0;
  118.         CDB12_BLK(&cdb.scl, bswap32(addr));
  119.         CDB12_LEN(&cdb.scl, bswap32(length));
  120.         CDB12_RSV(&cdb.scl, rsvd);
  121.         CDB12_CTL(&cdb.scl, control);
  122.  
  123.         scbp->sc_cmdpt = (caddr_t) SCL_AD(&cdb);
  124.         scbp->sc_cmdsz = SCL_SZ;
  125.         break;
  126.  
  127.     case 0xc0:
  128.     case 0xd0:
  129.     case 0x20:
  130.     case 0x30:
  131.     case 0x40:
  132.         /* 10-byte commands */
  133.         cdb.scm.sm_op = opcode;
  134.         cdb.scm.sm_res1 = param;
  135.         cdb.scm.sm_lun = 0;
  136.         CDB10_BLK(&cdb.scm, bswap32(addr));
  137.         CDB10_LEN(&cdb.scm, bswap16((word16_t) length));
  138.         CDB10_RSV(&cdb.scm, rsvd);
  139.         CDB10_CTL(&cdb.scm, control);
  140.  
  141.         scbp->sc_cmdpt = (caddr_t) SCM_AD(&cdb);
  142.         scbp->sc_cmdsz = SCM_SZ;
  143.         break;
  144.  
  145.     case 0x00:
  146.     case 0x10:
  147.         /* 6-byte commands */
  148.         cdb.scs.ss_op = opcode;
  149.         cdb.scs.ss_addr1 = param;
  150.         cdb.scs.ss_lun = 0;
  151.         CDB6_BLK(&cdb.scs, bswap16((word16_t) addr));
  152.         CDB6_LEN(&cdb.scs, (byte_t) length);
  153.         CDB6_CTL(&cdb.scs, control);
  154.  
  155.         scbp->sc_cmdpt = (caddr_t) SCS_AD(&cdb);
  156.         scbp->sc_cmdsz = SCS_SZ;
  157.         break;
  158.  
  159.     default:
  160.         if (app_data.scsierr_msg && prnerr)
  161.             fprintf(errfp, "0x%02x: Unknown SCSI opcode\n",
  162.                 opcode);
  163.         return FALSE;
  164.     }
  165.  
  166.     DBGDUMP("SCSI CDB bytes", (byte_t *) scbp->sc_cmdpt, scbp->sc_cmdsz);
  167.  
  168.     /* set up scsicmd */
  169.     scbp->sc_datapt = (caddr_t) buf;
  170.     scbp->sc_datasz = size;
  171.     scbp->sc_mode = (rw == READ_OP) ? SCB_READ : SCB_WRITE;
  172.     scbp->sc_time = 5000;    /* Allow 5 seconds */
  173.  
  174.     /* Send the command down via the "pass-through" interface */
  175.     while (ioctl(pthru_fd, SDI_SEND, &sb) < 0) {
  176.         if (errno == EAGAIN)
  177.             /* Wait a little while and retry */
  178.             sleep(1);
  179.         else {
  180.             if (app_data.scsierr_msg && prnerr)
  181.                 perror("SDI_SEND ioctl failed");
  182.             return FALSE;
  183.         }
  184.     }
  185.  
  186.     if (scbp->sc_comp_code != SDI_ASW) {
  187.         if (app_data.scsierr_msg && prnerr) {
  188.             fprintf(errfp,
  189.                 "CD audio: %s %s:\n%s=0x%x %s=0x%x %s=0x%x",
  190.                 "SCSI command fault on",
  191.                 app_data.device,
  192.                 "Opcode",
  193.                 opcode,
  194.                 "Completion_code",
  195.                 scbp->sc_comp_code,
  196.                 "Target_status",
  197.                 scbp->sc_status);
  198.         }
  199.  
  200.         /* Send Request Sense command */
  201.         cdb.scs.ss_op = OP_S_RSENSE;
  202.         cdb.scs.ss_addr1 = 0;
  203.         cdb.scs.ss_lun = 0;
  204.         CDB6_BLK(&cdb.scs, 0);
  205.         CDB6_LEN(&cdb.scs, SZ_RSENSE);
  206.         CDB6_CTL(&cdb.scs, 0);
  207.         scbp->sc_datapt = (caddr_t) &sense_data;
  208.         scbp->sc_datasz = SZ_RSENSE;
  209.         scbp->sc_mode = SCB_READ;
  210.         scbp->sc_cmdpt = (caddr_t) SCS_AD(&cdb);
  211.         scbp->sc_cmdsz = SCS_SZ;
  212.  
  213.         if (ioctl(pthru_fd, SDI_SEND, &sb) < 0 ||
  214.             sense_data.valid == 0) {
  215.             if (app_data.scsierr_msg && prnerr)
  216.                 fprintf(errfp, "\n");
  217.         }
  218.         else if (app_data.scsierr_msg && prnerr) {
  219.             fprintf(errfp, " Key=0x%x Code=0x%x Qual=0x%x\n",
  220.                 sense_data.key,
  221.                 sense_data.code,
  222.                 sense_data.qual);
  223.         }
  224.  
  225.         return FALSE;
  226.     }
  227.     return TRUE;
  228. }
  229.  
  230.  
  231. /*
  232.  * pthru_open
  233.  *    Open SCSI pass-through device
  234.  *
  235.  * Args:
  236.  *    path - device path name string
  237.  *
  238.  * Return:
  239.  *    TRUE - open successful
  240.  *    FALSE - open failed
  241.  */
  242. bool_t
  243. pthru_open(char *path)
  244. {
  245.     int        devfd;
  246.     dev_t        ptdev;
  247.     struct stat    stbuf;
  248.     char        errstr[ERR_BUF_SZ];
  249.  
  250.     /* Check for validity of device node */
  251.     if (stat(path, &stbuf) < 0) {
  252.         sprintf(errstr, app_data.str_staterr, path);
  253.         cd_fatal_popup(app_data.str_fatal, errstr);
  254.         return FALSE;
  255.     }
  256.     if (!S_ISCHR(stbuf.st_mode)) {
  257.         sprintf(errstr, app_data.str_noderr, path);
  258.         cd_fatal_popup(app_data.str_fatal, errstr);
  259.         return FALSE;
  260.     }
  261.  
  262.     /* Open CD-ROM device */
  263.     if ((devfd = open(path, O_RDONLY)) < 0) {
  264.         DBGPRN(errfp, "Cannot open %s: errno=%d\n", path, errno);
  265.         return FALSE;
  266.     }
  267.  
  268.     /* Get pass-through interface device number */
  269.     if (ioctl(devfd, B_GETDEV, &ptdev) < 0) {
  270.         DBGPRN(errfp, "B_GETDEV ioctl failed: errno=%d\n", errno);
  271.         close(devfd);
  272.         return FALSE;
  273.     }
  274.  
  275.     close(devfd);
  276.  
  277.     /* Make pass-through interface device node */
  278.     sprintf(ptpath, "%s/pass.%x", TEMP_DIR, ptdev);
  279.  
  280.     if (mknod(ptpath, S_IFCHR | 0700, ptdev) < 0) {
  281.         if (errno == EEXIST) {
  282.             unlink(ptpath);
  283.  
  284.             if (mknod(ptpath, S_IFCHR | 0700, ptdev) < 0) {
  285.                 DBGPRN(errfp, "Cannot mknod %s: errno=%d\n",
  286.                     ptpath, errno);
  287.                 return FALSE;
  288.             }
  289.         }
  290.         else {
  291.             DBGPRN(errfp, "Cannot mknod %s: errno=%d\n",
  292.                 ptpath, errno);
  293.             return FALSE;
  294.         }
  295.     }
  296.  
  297.     /* Open pass-through device node */
  298.     if ((pthru_fd = open(ptpath, O_RDONLY)) < 0) {
  299.         DBGPRN(errfp, "Cannot open %s: errno=%d\n", ptpath, errno);
  300.         return FALSE;
  301.     }
  302.  
  303.     return TRUE;
  304. }
  305.  
  306.  
  307. /*
  308.  * pthru_close
  309.  *    Close SCSI pass-through device
  310.  *
  311.  * Args:
  312.  *    Nothing.
  313.  *
  314.  * Return:
  315.  *    Nothing.
  316.  */
  317. void
  318. pthru_close(void)
  319. {
  320.     if (pthru_fd >= 0) {
  321.         close(pthru_fd);
  322.         pthru_fd = -1;
  323.     }
  324.  
  325.     if (ptpath[0] != '\0')
  326.         unlink(ptpath);
  327. }
  328.  
  329.  
  330. /*
  331.  * pthru_vers
  332.  *    Return OS Interface Module version string
  333.  *
  334.  * Args:
  335.  *    Nothing.
  336.  *
  337.  * Return:
  338.  *    Module version text string.
  339.  */
  340. char *
  341. pthru_vers(void)
  342. {
  343. #ifdef _FTX
  344.     return ("OS Interface module (for Stratus FTX 3.x)\n");
  345. #else
  346.     return ("OS Interface module (for UNIX SVR4-PDI/x86)\n");
  347. #endif
  348. }
  349.  
  350. #endif    /* i386 _FTX __hppa */
  351.  
  352. #ifdef MOTOROLA
  353. /*
  354.  *   Motorola 88k UNIX SVR4 support
  355.  *
  356.  *   Contributing author: Mark Scott
  357.  *   E-mail: mscott@urbana.mcd.mot.com
  358.  *
  359.  *   Note: Audio CDs sometimes produce "Blank check" warnings on the console, 
  360.  *         just ignore these.
  361.  *
  362.  *   This software fragment contains code that interfaces the CD
  363.  *   player application to the System V Release 4 operating system
  364.  *   from Motorola.  The name "Motorola" is used here for identification
  365.  *   purposes only.
  366.  */
  367.  
  368.  
  369. /*
  370.  * pthru_send
  371.  *    Build SCSI CDB and sent command to the device.
  372.  *
  373.  * Args:
  374.  *    opcode - SCSI command opcode
  375.  *    addr - The "address" portion of the SCSI CDB
  376.  *    buf - Pointer to data buffer
  377.  *    size - Number of bytes to transfer
  378.  *    rsvd - The "reserved" portion of the SCSI CDB
  379.  *    length - The "length" portion of the SCSI CDB
  380.  *    param - The "param" portion of the SCSI CDB
  381.  *    control - The "control" portion of the SCSI CDB
  382.  *    rw - Data transfer direction flag (READ_OP or WRITE_OP)
  383.  *    prnerr - Whether an error message should be displayed
  384.  *         when a command fails
  385.  *
  386.  * Return:
  387.  *    TRUE - command completed successfully
  388.  *    FALSE - command failed
  389.  */
  390. bool_t
  391. pthru_send(
  392.     byte_t        opcode,
  393.     word32_t    addr,
  394.     byte_t        *buf,
  395.     word32_t    size,
  396.     byte_t        rsvd,
  397.     word32_t    length,
  398.     byte_t        param,
  399.     byte_t        control,
  400.     byte_t        rw,
  401.     bool_t        prnerr
  402. )
  403. {
  404.     char            scsistat = '\0',
  405.                 *tmpbuf;
  406.     int            cdb_l = 0,
  407.                 i;
  408.     long            residual = 0L;
  409.     unsigned long        errinfo  = 0L,
  410.                 ccode = 0L;
  411.     struct scsi_pass    spass,
  412.                 *sp = &spass;
  413.     struct ext_sense    sense,
  414.                 *esp = &sense;
  415.  
  416.     if (pthru_fd < 0 || scsipt_notrom_error)
  417.         return FALSE;
  418.  
  419.     /* Zero out the struct */
  420.     memset(sp, 0, sizeof(struct scsi_pass));
  421.     memset(esp, 0, sizeof(struct ext_sense));
  422.  
  423.     /* Setup passthru structure */
  424.     sp->resid = &residual;
  425.     sp->sense_data = esp;
  426.     sp->status = &scsistat;
  427.     sp->error_info = &errinfo;
  428.     sp->ctlr_code = &ccode;
  429.     sp->xfer_len = (unsigned long) size;
  430.  
  431.     /* Align on a page boundary */
  432.     tmpbuf = NULL;
  433.     if (sp->xfer_len > 0) {
  434.         tmpbuf = (char *) malloc(2 * NBPP);
  435.         sp->data = tmpbuf;
  436.         sp->data += NBPP - ((unsigned int) sp->data & (NBPP - 1));
  437.     }
  438.     else
  439.         sp->data = tmpbuf;
  440.  
  441.  
  442.     if (rw == WRITE_OP && sp->xfer_len > 0)    /* Write operation */
  443.         memcpy(sp->data, buf, sp->xfer_len);
  444.  
  445.     /* Set up SCSI CDB */
  446.     switch (opcode & SPT_CDB_LEN) {
  447.     case 0xa0:
  448.     case 0xe0:
  449.         /* 12-byte commands */
  450.         cdb_l = 0xc0;
  451.         sp->cdb[0] = opcode;
  452.         sp->cdb[1] = param;
  453.         sp->cdb[2] = (addr >> 24) & 0xff;
  454.         sp->cdb[3] = (addr >> 16) & 0xff;
  455.         sp->cdb[4] = (addr >> 8) & 0xff;
  456.         sp->cdb[5] = (addr & 0xff);
  457.         sp->cdb[6] = (length >> 24) & 0xff;
  458.         sp->cdb[7] = (length >> 16) & 0xff;
  459.         sp->cdb[8] = (length >> 8) & 0xff;
  460.         sp->cdb[9] = length & 0xff;
  461.         sp->cdb[10] = rsvd;
  462.         sp->cdb[11] = control;
  463.         break;
  464.  
  465.     case 0xc0:
  466.     case 0xd0:
  467.     case 0x20:
  468.     case 0x30:
  469.     case 0x40:
  470.         /* 10-byte commands */
  471.         cdb_l = 0xa0;
  472.         sp->cdb[0] = opcode;
  473.         sp->cdb[1] = param;
  474.         sp->cdb[2] = (addr >> 24) & 0xff;
  475.         sp->cdb[3] = (addr >> 16) & 0xff;
  476.         sp->cdb[4] = (addr >> 8) & 0xff;
  477.         sp->cdb[5] = addr & 0xff;
  478.         sp->cdb[6] = rsvd;
  479.         sp->cdb[7] = (length >> 8) & 0xff;
  480.         sp->cdb[8] = length & 0xff;
  481.         sp->cdb[9] = control;
  482.         break;
  483.  
  484.     case 0x00:
  485.     case 0x10:
  486.         /* 6-byte commands */
  487.         cdb_l = 0x60;
  488.         sp->cdb[0] = opcode;
  489.         sp->cdb[1] = param;
  490.         sp->cdb[2] = (addr >> 8) & 0xff;
  491.         sp->cdb[3] = addr & 0xff;
  492.         sp->cdb[4] = length & 0xff;
  493.         sp->cdb[5] = control;
  494.         break;
  495.  
  496.     default:
  497.         if (app_data.scsierr_msg && prnerr)
  498.             fprintf(errfp, "0x%02x: Unknown SCSI opcode\n",
  499.                 opcode);
  500.         if (tmpbuf != NULL)
  501.             free(tmpbuf);
  502.  
  503.         return FALSE;
  504.     }
  505.  
  506.  
  507.     /* Check CDB length & flags */
  508.  
  509.     if (!SPT_CHK_CDB_LEN(cdb_l))
  510.         fprintf(errfp, "%d: invalid CDB length\n", cdb_l);
  511.  
  512.     sp->flags = cdb_l | SPT_ERROR_QUIET;
  513.     if (rw == READ_OP)
  514.         sp->flags |= SPT_READ;
  515.  
  516.     if (SPT_CHK_FLAGS(cdb_l))
  517.         fprintf(errfp, "0x%2x: bad CDB flags\n", sp->flags);
  518.  
  519.     DBGDUMP("SCSI CDB bytes", (byte_t *) sp->cdb, cdb_l);
  520.  
  521.     /* Send the command down via the "pass-through" interface */
  522.     if (ioctl(pthru_fd, DKPASSTHRU, sp) < 0) {
  523.         if (app_data.scsierr_msg && prnerr)
  524.             perror("DKPASSTHRU ioctl failed");
  525.  
  526.         if (tmpbuf != NULL)
  527.             free(tmpbuf);
  528.  
  529.         return FALSE;
  530.     }
  531.  
  532.     if (*sp->error_info != SPTERR_NONE) {
  533.         if (*sp->error_info != SPTERR_SCSI && *sp->status != 2 &&
  534.             app_data.scsierr_msg && prnerr) {
  535.  
  536.             /* Request Sense is done automatically by the driver */
  537.             fprintf(errfp, "CD audio: %s\n",
  538.                 "SCSI command fault on",
  539.                 app_data.device);
  540.  
  541.             fprintf(errfp,
  542.                 "CD audio: %s %s:\n%s=0x%x %s=0x%x %s=0x%x\n",
  543.                 "SCSI command fault on",
  544.                 app_data.device,
  545.                 "opcode",
  546.                 opcode,
  547.                 "xfer_len",
  548.                 sp->xfer_len,
  549.                 "err_info",
  550.                 *sp->error_info);
  551.  
  552.             fprintf(errfp, "%s=0x%x %s=0x%x %s=0x%x\n",
  553.                 "ctlr_code",
  554.                 *sp->ctlr_code,
  555.                 "status",
  556.                 *sp->status,
  557.                 "resid",
  558.                 *sp->resid);
  559.         }
  560.  
  561.         if (tmpbuf != NULL)
  562.             free(tmpbuf);
  563.  
  564.         return FALSE;
  565.     }
  566.  
  567.     /* pass the data back to caller */
  568.     if (sp->xfer_len > 0 && rw == READ_OP)    /* read operation */
  569.         memcpy(buf, sp->data, sp->xfer_len);
  570.  
  571.     if (tmpbuf != NULL)
  572.         free(tmpbuf);
  573.  
  574.     return TRUE;
  575. }
  576.  
  577.  
  578. /*
  579.  * pthru_open
  580.  *    Open SCSI pass-through device
  581.  *
  582.  * Args:
  583.  *    path - device path name string
  584.  *
  585.  * Return:
  586.  *    TRUE - open successful
  587.  *    FALSE - open failed
  588.  */
  589. bool_t
  590. pthru_open(char *path)
  591. {
  592.     struct stat    stbuf;
  593.     char        errstr[ERR_BUF_SZ];
  594.     int        i;
  595.     bool_t        ret;
  596.  
  597.     /* Check for validity of device node */
  598.     if (stat(path, &stbuf) < 0) {
  599.         sprintf(errstr, app_data.str_staterr, path);
  600.         cd_fatal_popup(app_data.str_fatal, errstr);
  601.         return FALSE;
  602.     }
  603.     if (!S_ISCHR(stbuf.st_mode)) {
  604.         sprintf(errstr, app_data.str_noderr, path);
  605.         cd_fatal_popup(app_data.str_fatal, errstr);
  606.         return FALSE;
  607.     }
  608.  
  609.     /* Open CD-ROM device */
  610.     if ((pthru_fd = open(path, O_RDONLY | O_NDELAY | O_EXCL)) < 0) {
  611.         DBGPRN(errfp, "Cannot open %s: errno=%d\n", path, errno);
  612.         return FALSE;
  613.     }
  614.  
  615.     /* Motorola hack:  The CD-ROM driver allows the open to succeed
  616.      * even if there is no CD loaded.  We test for the existence of
  617.      * a disc with scsipt_tst_unit_rdy().
  618.      */
  619.     for (i = 0; i < 3; i++) {
  620.                 if ((ret = scsipt_tst_unit_rdy()) == TRUE)
  621.                         break;
  622.         }
  623.         if (!ret) {
  624.                 /* No CD loaded */
  625.                 pthru_close();
  626.                 return FALSE;
  627.         }
  628.  
  629.     return TRUE;
  630. }
  631.  
  632.  
  633. /*
  634.  * pthru_close
  635.  *    Close SCSI pass-through device
  636.  *
  637.  * Args:
  638.  *    Nothing.
  639.  *
  640.  * Return:
  641.  *    Nothing.
  642.  */
  643. void
  644. pthru_close(void)
  645. {
  646.     if (pthru_fd >= 0) {
  647.         close(pthru_fd);
  648.         pthru_fd = -1;
  649.     }
  650. }
  651.  
  652.  
  653. /*
  654.  * pthru_vers
  655.  *    Return OS Interface Module version string
  656.  *
  657.  * Args:
  658.  *    Nothing.
  659.  *
  660.  * Return:
  661.  *    Module version text string.
  662.  */
  663. char *
  664. pthru_vers(void)
  665. {
  666.     return ("OS Interface module (for Motorola SVR4-m88k)\n");
  667. }
  668.  
  669. #endif    /* MOTOROLA */
  670.  
  671. #endif    /* SVR4 sun DI_SCSIPT DEMO_ONLY */
  672.  
  673.